﻿#include <QTimer>
#include "searchdevice.h"


SearchDevice::SearchDevice(BTDevice *bt_device,QDialog *parent) :
    QDialog{parent},
    btDevice(bt_device)
{
    btn_search = new QPushButton;
    btn_disconnect = new QPushButton;
    list_device = new QListWidget;
    list_service = new QListWidget;
    list_characteristic = new QListWidget;
//    table_device = new QTableWidget;
    layout_grid = new QGridLayout;

    btn_disconnect->setText(tr("Disconnect"));
    btn_disconnect->setEnabled(false);
    btn_search->setText(tr("Start Search"));
    list_device->setEnabled(false);
    layout_grid->addWidget(list_device,1,1,1,3);
    layout_grid->addWidget(list_service,2,1,1,3);
    layout_grid->addWidget(list_characteristic,3,1,1,3);
    layout_grid->addWidget(btn_search,4,2,1,1);
    layout_grid->addWidget(btn_disconnect,4,3,1,1);
    setLayout(layout_grid);
    resize(300,400);

//    vboxlayout = new QVBoxLayout;
//    vboxlayout->addWidget(list_service);
//    dig_service.setLayout(vboxlayout);
////    dig_service.setWindowModality(Qt::ApplicationModal);

    searching = false;
    connect(btn_search,&QPushButton::clicked,this,&SearchDevice::on_click_btn_search);
    connect(btn_disconnect,&QPushButton::clicked,this,&SearchDevice::on_btn_disconnect);
    connect(list_device, &QListWidget::itemActivated,this, &SearchDevice::on_list_double_clicked);
    connect(list_device, &QListWidget::itemClicked,this, &SearchDevice::on_list_clicked);
    connect(list_service,&QListWidget::itemActivated,this,&SearchDevice::on_list_service_double_clicked);
//    connect(&dig_service,&QDialog::finished,this,&SearchDevice::on_service_dialog_finished);

    connect(btDevice,&BTDevice::discoveredDevice,this,&SearchDevice::on_add_bt_device);
    connect(btDevice,&BTDevice::devicesUpdated,this,&SearchDevice::on_device_search_finished);
    connect(btDevice,&BTDevice::servicesUpdated,this,&SearchDevice::on_service_update);
    connect(btDevice,&BTDevice::characteristicsUpdated,this,&SearchDevice::on_service_characteristics_updated);
    connect(btDevice,&BTDevice::disconnected,this,&SearchDevice::on_ble_device_is_disconnect);

//    QList<QObject *> dev = btDevice->getDevices().value<QList<QObject *>>();
//    for (auto d:dev) {
//        if (auto device = qobject_cast<BTDeviceInfo *>(d)) {
////            qDebug() << "name:" <<device->getName() << "addr:" << device->getAddress();
//            on_add_bt_device(device->getDevice());
//        }
//    }
    this->hide();
}

void SearchDevice::write_data_to_device(QByteArray &data)
{
    if(bleWriteChar.isValid())
    {
        uint32_t bytes = data.size();
        uint32_t count = (bytes + 19) / 20;
        uint32_t index = 0 ;
        uint32_t get ;
        while(count)
        {
            get = bytes >= 20 ? 20 : bytes;
            QByteArray a(data.data() + index,get);
            current_service->writeCharacteristic(bleWriteChar,a);
            index += get;
            bytes -= get;
            count --;
        }
    }
}

void SearchDevice::on_add_bt_device(const QBluetoothDeviceInfo &info)
{
#ifdef Q_OS_MAC
    //    QString label = QString("%1 %2").arg(info.name()).arg(info.deviceUuid().toString().remove(QLatin1Char('{')).remove(QLatin1Char('}')));
    QString label = QString("%1 %2").arg(info.name()).arg(info.deviceUuid().toString());
#else
//    QString label = QString("%1,%2").arg(info.name()).arg(info.address().toString());
    QString label = QString("%1").arg(info.name());
    device_address_list.append(info.address());
#endif
    QList<QListWidgetItem *> items = list_device->findItems(label, Qt::MatchExactly);
    if (items.empty()) {
        QListWidgetItem *item = new QListWidgetItem(label);
        //        item->setForeground(QColor(Qt::black));
        list_device->addItem(item);
    }
}

void SearchDevice::on_click_btn_search()
{
    if(searching == false)
    {
        device_address_list.clear();
        list_device->clear();
        btDevice->startDeviceDiscovery();
        searching = true;
        list_device->setEnabled(false);
        btn_search->setText(tr("Stop"));
    }
    else
    {
        btDevice->stopDeviceDiscovery();
        searching = false;
        btn_search->setText(tr("Start Search"));
    }
}

void SearchDevice::on_device_search_finished()
{
    searching = false;
    list_device->setEnabled(true);
    btn_search->setText(tr("Start Search"));
}

void SearchDevice::on_list_double_clicked(QListWidgetItem *item)
{
    int i = list_device->currentRow();
    qDebug() << "connect to:" << device_address_list.at(i).toString();

    QString addr = device_address_list.at(i).toString();
    if(!addr.isNull())
    {
        btDevice->scanServices(addr);
    }
    else
    {
        qWarning() << "device address is null !!!";
    }
    service_uuid.clear();
    list_service->clear();
    list_characteristic->clear();
    btn_disconnect->setEnabled(false);
//    dig_service = new QDialog();
//    dig_service->setAttribute(Qt::WA_DeleteOnClose,true);
//    dig_service.show();
//    dig_service.setWindowTitle(item->text() + " services");
//    dig_service.exec();
}

void SearchDevice::on_list_clicked(QListWidgetItem *item)
{
//    QString text = item->text();
    //    qDebug() << "clicked:" << text;
}

void SearchDevice::on_service_update()
{
    service_uuid.clear();
    list_service->clear();
    QList<QObject *> sev = btDevice->getServices().value<QList<QObject *>>();
    for (auto d:sev) {
        if (auto info = qobject_cast<BTServiceInfo *>(d)) {
//            qDebug() << "name:" <<info->getName() << "---type:" << info->getType() << "---uuid:" << info->getUuid();
            service_uuid.append(info->getUuid());
            QString str = "service name:" + info->getName() + ", type:" + info->getType() + ", uuid:" + info->getUuid();
            QListWidgetItem *item = new QListWidgetItem(str);
            list_service->addItem(item);
            qDebug() << "---> add serverce:" << item->text();
        }
    }
//    list_service->update();
}

void SearchDevice::on_list_service_double_clicked(QListWidgetItem *item)
{
    int i = list_service->currentRow();

    QVariant ss = btDevice->getServices();
    QList<QObject *> services = ss.value<QList<QObject *>>();
    if(!services.isEmpty())
    {
        //for (int i=0; i<services.count(); i++)
        {
            BTServiceInfo *serv = (BTServiceInfo *)services.at(i);
            current_service = serv->service();

#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
            disconnect(current_service,&QLowEnergyService::errorOccurred,this,&SearchDevice::on_ble_service_error_occurred);
            connect(current_service,&QLowEnergyService::errorOccurred,this,&SearchDevice::on_ble_service_error_occurred);
#else
            disconnect(current_service,static_cast<void (QLowEnergyService::*)(QLowEnergyService::ServiceError)>(&QLowEnergyService::error),this,&SearchDevice::on_ble_service_error_occurred);
            connect(current_service,static_cast<void (QLowEnergyService::*)(QLowEnergyService::ServiceError)>(&QLowEnergyService::error),this,&SearchDevice::on_ble_service_error_occurred);
#endif
            disconnect(current_service,&QLowEnergyService::stateChanged,this,&SearchDevice::on_ble_service_state_changed);
            connect(current_service,&QLowEnergyService::stateChanged,this,&SearchDevice::on_ble_service_state_changed);

            qDebug() << "connect to service: " << serv->getUuid();
//            QVariant chars = btDevice->getCharacteristics();
//            QList<QObject *> characteristics = chars.value<QList<QObject *>>();
//            characteristics.clear();
            btDevice->connectToService(serv->getUuid());
        }
    }
}

void SearchDevice::on_service_dialog_finished(int result)
{
//    qDebug() << "service dialog result:" << result;
//    if(result == 0)
//    {
//        btDevice->disconnectFromDevice();
//    }
//    else
//    {
//        done(1);
//    }
}

void SearchDevice::on_service_characteristics_updated()
{
    qDebug() << ">>>>>>" << __func__ << "<<<<<<";
    charCount = 0 ;
    list_characteristic->clear();
    QVariant chars = btDevice->getCharacteristics();
    QList<QObject *> characteristics = chars.value<QList<QObject *>>();
    if(!characteristics.isEmpty())
    {
        for(int i=0; i<characteristics.count(); i++)
        {
            BTCharacteristicInfo *charInfo = (BTCharacteristicInfo *)characteristics.at(i);
            QString strChar;
            strChar = "name:" + charInfo->getName() + ",uuid:" + charInfo->getUuid() + ",value:" + charInfo->getValue() + ",permission:" + charInfo->getPermissionToString();
            qDebug() << strChar;
            list_characteristic->addItem(strChar);
            int perm = charInfo->getPermission();
            if((perm & QLowEnergyCharacteristic::Notify) && (perm & QLowEnergyCharacteristic::Read))
            {
                qDebug() << "set read char";
                bleReadChar = charInfo->getCharacteristic();
                charCount ++;
            }
            else if(perm & QLowEnergyCharacteristic::Write)
            {
                qDebug() << "set write char";
                bleWriteChar = charInfo->getCharacteristic();
                charCount ++;
            }
        }
        if(charCount > 0)
        {
//            emit bleConnectSuccess();
//            pBtnBTDisconnect->setEnabled(true);

//配置MTU
//            for(quint16 i=0x1000; i<0x1820; i++)
//            {
//                QBluetoothUuid uuid(i);
//                QLowEnergyDescriptor att = bleReadChar.descriptor(uuid);
//                if(att.isValid())
//                {
//                    qDebug() << "uuid is valid:" << uuid.toString();
//                }
//                else
//                {
//                    qDebug() << "uuid is not valid:" << uuid.toString();
//                }
//            }

//配置notify
#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
            QLowEnergyDescriptor notification = bleReadChar.descriptor(QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration);
#else
            QLowEnergyDescriptor notification = bleReadChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
#endif
            if(notification.isValid())
            {
                disconnect(current_service,&QLowEnergyService::characteristicChanged,this,&SearchDevice::on_ble_service_characteristic_changed);
                connect(current_service,&QLowEnergyService::characteristicChanged,this,&SearchDevice::on_ble_service_characteristic_changed);
                //开启广播 ，这样程序就可以接收蓝牙设备主动发送过来的数据了
                current_service->writeDescriptor(notification,QByteArray::fromHex("0100"));
            }
            else
            {
                qDebug() << "notification is not valid";
            }
            disconnect(current_service,&QLowEnergyService::descriptorRead,this,&SearchDevice::on_ble_service_descriptor_read);
            connect(current_service,&QLowEnergyService::descriptorRead,this,&SearchDevice::on_ble_service_descriptor_read);
            disconnect(current_service,&QLowEnergyService::descriptorWritten,this,&SearchDevice::on_ble_service_descriptor_written);
            connect(current_service,&QLowEnergyService::descriptorWritten,this,&SearchDevice::on_ble_service_descriptor_written);
            disconnect(current_service,&QLowEnergyService::characteristicRead,this,&SearchDevice::on_ble_service_characteristic_read);
            connect(current_service,&QLowEnergyService::characteristicRead,this,&SearchDevice::on_ble_service_characteristic_read);
            disconnect(current_service,&QLowEnergyService::characteristicWritten,this,&SearchDevice::on_ble_service_characteristic_written);
            connect(current_service,&QLowEnergyService::characteristicWritten,this,&SearchDevice::on_ble_service_characteristic_written);
            btn_disconnect->setEnabled(true);
            QTimer::singleShot(500, this, &SearchDevice::device_is_connect);
//            QTimer::singleShot(2000, this, &SearchDevice::on_tim_read_char);
//            emit device_is_connect();
        }
        else
        {
            qDebug() << "not connect service 0xffc1 or 0xffc2";
        }
    }
}

void SearchDevice::on_ble_service_error_occurred(QLowEnergyService::ServiceError newError)
{
    qDebug() << ">>>>>>" << __func__ << "<<<<<<";
    qDebug() <<  newError;
}

void SearchDevice::on_ble_service_state_changed(QLowEnergyService::ServiceState newState)
{
    qDebug() << ">>>>>>" << __func__ << "<<<<<<";
    qDebug() << newState;
}

void SearchDevice::on_ble_service_characteristic_changed(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)
{
    qDebug() << ">>>>>>" << __func__ << "<<<<<<";
    qDebug() << "receive size:" << newValue.size();
    qDebug() << "receive data:" << newValue.toHex(' ');

}

void SearchDevice::on_ble_service_descriptor_read(const QLowEnergyDescriptor &descriptor, const QByteArray &value)
{
    qDebug() << ">>>>>>" << __func__ << "<<<<<<";
    qDebug() << "uuid:" << descriptor.uuid().toString() ;
    qDebug() << "type:" << QString("%1").arg((quint16)descriptor.type(),0,16);
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
    qDebug() << "handle:" << descriptor.handle();
#endif
    qDebug() << "name:" << descriptor.name();
    qDebug() << "value:" << value.toHex();

}

void SearchDevice::on_ble_service_descriptor_written(const QLowEnergyDescriptor &descriptor, const QByteArray &newValue)
{
    qDebug() << ">>>>>>" << __func__ << "<<<<<<";
    qDebug() << "uuid:" << descriptor.uuid().toString() ;
    qDebug() << "type:" << QString("%1").arg((quint16)descriptor.type(),0,16);
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
    qDebug() << "handle:" << descriptor.handle();
#endif
    qDebug() << "name:" << descriptor.name();
    qDebug() << "value:" << newValue.toHex();

}

void SearchDevice::on_ble_service_characteristic_written(const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)
{
    qDebug() << ">>>>>>" << __func__ << "<<<<<<";
    qDebug() << "write size:" << newValue.size();
    qDebug() << "write data:" << newValue.toHex();

}

void SearchDevice::on_ble_service_characteristic_read(const QLowEnergyCharacteristic &characteristic, const QByteArray &value)
{
    qDebug() << ">>>>>>" << __func__ << "<<<<<<";
    qDebug() << "receive size:" << value.size();
    qDebug() << "receive data:" << value.toHex(' ');

}

void SearchDevice::on_ble_device_is_disconnect(const QBluetoothAddress &address)
{
    qDebug() << ">>>>>>" << __func__ << "<<<<<<";
    qDebug() << "device is disconnect:" + address.toString();
    list_service->clear();
    list_characteristic->clear();
    btn_disconnect->setEnabled(false);
}

void SearchDevice::on_btn_disconnect()
{
    btDevice->disconnectFromDevice();
}

void SearchDevice::on_tim_read_char()
{
    current_service->readCharacteristic(bleReadChar);
}

